///! 缓存库
use super::*;
use js_sys::JSON;
use json::object;
use web_sys::Storage;

#[wasm_bindgen(typescript_custom_section)]
const ITEXT_STYLE: &'static str = r#"
interface Options {
    /** 键前缀 */
    namespace: string;
    /** 存储类型 session, local, memory memory暂时未实现 */
    storage: string;
}
"#;

#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(typescript_type = "Options")]
    pub type Options;

    # [wasm_bindgen (structural , method , getter , js_class = "Options" , js_name = namespace)]
    pub fn namespace(this: &Options) -> String;
    # [wasm_bindgen (structural , method , getter , js_class = "Options" , js_name = storage)]
    pub fn storage(this: &Options) -> String;
}

#[wasm_bindgen]
pub struct YnStorage {
    /// 编码
    code: String,
    storage: Option<Storage>,
}

#[wasm_bindgen]
impl YnStorage {
    /// YnStorage localStorage 存储库
    /// # Example
    /// ``` js
    /// import init, { YnStorage } from "wasm";
    ///
    /// init().then(()=>{
    ///   const storageOptions = {
    ///       namespace: 'Yn_', // key prefix
    ///       storage: 'local' // storage name session, local, memory
    ///    };
    ///
    ///    let ls = new YnStorage(storageOptions);
    ///    ls.set("key","YN", 5000);
    /// })
    /// ```
    #[wasm_bindgen(constructor)]
    pub fn new(option: Options) -> Self {
        let code = option.namespace();
        let o_window = web_sys::window().expect("no global `window` exists");
        let storage: Option<Storage> = match option.storage().as_str() {
            "local" => o_window
                .local_storage()
                .expect("no global `local_storage` exists"),
            "session" => o_window
                .session_storage()
                .expect("no global `session_storage` exists"),
            _ => None,
        };
        match storage {
            Some(storage) => Self {
                code,
                storage: Some(storage),
            },
            None => Self {
                code,
                storage: None,
            },
        }
    }

    /// 添加/设置一个key - 数据
    ///
    /// # Example
    /// ``` js
    /// import init, { YnStorage } from "wasm";
    ///
    /// init().then(()=>{
    ///   const storageOptions = {
    ///       namespace: 'Yn_', // key prefix
    ///       storage: 'local' // storage name session, local, memory
    ///    };
    ///
    ///    let ls = new YnStorage(storageOptions);
    ///    ls.set("key","value", 5000);
    /// })
    /// ```
    pub fn set(&self, key: String, value: JsValue, expire: Option<i32>) {
        let key = format!("{}{}", self.code, key);
        let y_data = YStorage { key, value, expire };
        // log(&format!("{:?},code = {:?}", y_data, self.code));
        match &self.storage {
            Some(storage) => match storage.set(&y_data.key, y_data.to_string().as_str()) {
                Ok(_) => {}
                Err(e) => error(&format!("window暂无local_storage,错误:{:?}", e)),
            },
            None => error(&format!("存储库未注册，你可能需要 new YnStorage")),
        };
    }

    /// 获取一个值
    ///
    /// # Example
    /// ``` js
    /// import init, { YnStorage } from "wasm";
    ///
    /// init().then(()=>{
    ///   const storageOptions = {
    ///       namespace: 'Yn_', // key prefix
    ///       storage: 'local' // storage name session, local, memory
    ///    };
    ///
    ///    let ls = new YnStorage(storageOptions);
    ///    ls.set("key","YN", 5000);
    ///    ls.get("key") // YN
    /// })
    /// ```
    pub fn get(&self, key: String) -> JsValue {
        let key = format!("{}{}", self.code, key);

        match &self.storage {
            Some(storage) => match storage.get(&key) {
                Ok(value) => match value {
                    Some(v) => {
                        match json::parse(&v) {
                            Ok(v) => {
                                // 如果存在 expire 则进行判断是否过期
                                if let Some(expire) = v["expire"].as_i64() {
                                    if date_time::get_local_timestamp() > expire {
                                        self.remove(key);
                                        return JsValue::UNDEFINED;
                                    }
                                }
                                let value = v["value"].to_string();
                                match JSON::parse(&value) {
                                    Ok(value) => {
                                        return value;
                                    }
                                    Err(_) => {
                                        // error(&format!("转换失败:{:?}", e));
                                        return JsValue::from_str(&value);
                                    }
                                }
                            }
                            Err(e) => {
                                error(&format!("错误 {:?}", e));
                                return JsValue::NULL;
                            }
                        }
                    }
                    None => {
                        return JsValue::NULL;
                    }
                },
                Err(e) => {
                    error(&format!("window暂无local_storage,错误:{:?}", e));
                    return JsValue::NULL;
                }
            },
            None => {
                error(&format!("window暂无local_storage"));
                return JsValue::NULL;
            }
        };
    }

    /// 移除相关key
    pub fn remove(&self, key: String) -> bool {
        let key = format!("{}{}", self.code, key);
        match &self.storage {
            Some(storage) => match storage.remove_item(&key) {
                Ok(_) => {
                    return true;
                }
                Err(e) => {
                    error(&format!("移除{}失败,错误信息:{:?}", key, e));
                    return false;
                }
            },
            None => {
                error(&format!("存储库未注册，你可能需要new YnStorage"));
                return false;
            }
        };
    }

    /// 清空存储库所有的值
    pub fn clear(&self) -> bool {
        match &self.storage {
            Some(storage) => match storage.clear() {
                Ok(_) => return true,
                Err(e) => {
                    error(&format!("清除所有key执行失败,错误信息:{:?}", e));
                    return false;
                }
            },
            None => {
                error(&format!("存储库未注册，你可能需要new YnStorage"));
                return false;
            }
        }
    }

    /// 获取当前存储长度
    pub fn length(&self) -> u32 {
        match &self.storage {
            Some(storage) => match storage.length() {
                Ok(value) => return value,
                Err(e) => {
                    error(&format!("获取存储库长度失败,错误信息:{:?}", e));
                    return 0;
                }
            },
            None => {
                error(&format!("存储库未注册，你可能需要new YnStorage"));
                return 0;
            }
        }
    }
}

/// 存储数据格式
#[derive(Debug, Default)]
struct YStorage {
    key: String,
    value: JsValue,
    expire: Option<i32>,
}

impl YStorage {
    fn to_string(&self) -> String {
        let mut obj = object! {};
        obj.insert("key", self.key.clone()).unwrap();
        match &self.value.as_f64() {
            Some(value) => obj.insert("value", *value).unwrap(),
            None => {
                if self.value.is_null() || self.value.is_undefined() {
                    // TODO 此处是否需要删除key？
                } else if self.value.is_function() {
                    error(&format!("暂不支持js[`回调函数`]存储!-- {:?}", self.value))
                } else if self.value.is_symbol() {
                    error(&format!("暂不支持js[`Symbol`]存储!-- {:?}", self.value))
                } else if self.value.is_string() {
                    let value = self.value.as_string().unwrap();
                    obj.insert("value", value).unwrap();
                } else {
                    let value = JSON::stringify(&self.value).unwrap().as_string().unwrap();
                    obj.insert("value", value).unwrap();
                }
            }
        }
        if self.expire.is_some() {
            let expire = self.expire.clone().unwrap();
            let now = date_time::get_local_timestamp() + expire as i64;
            obj.insert("expire", now).unwrap();
        }
        obj.to_string()
    }
}
